package com.gitee.apanlh.util.unit;

import com.gitee.apanlh.exp.UnknownTypeException;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.reflection.ClassConvertUtils;
import com.gitee.apanlh.util.valid.Assert;

/**	
 * 	数据转换长度
 * 	
 * 	@author Pan
 */
public class DataSize {
	
	/** 1024B (1KB) */
	static final long B1 = 0x400L;
	/** 1048576B (1MB) */
	static final long B2 = 0x100000L;
	/** 1073741824B (1GB) */
	static final long B3 = 0x40000000L;
	/** 1099511627776B (1TB) */
	static final long B4 = 0x10000000000L;
	/** 1125899906842624B (1PB) */
	static final long B5 = 0x4000000000000L;
	
	/** 字符序列 */
	private CharSequence charSequence;
	/** 长度 */
	private Long len;
	/** 转换单位 */
	private DataSizeUnit toUnit;
	
	/**
	 * 	构造函数
	 * 
	 * 	@author Pan
	 */
	DataSize() {
		//	不允许外部实例
		super();
	}
	
	/**	
	 * 	构造函数-自定义字符序列
	 * 	<br>动态解析
	 * 	
	 * 	@author Pan
	 * 	@param 	charSequence	字符序列
	 */
	DataSize(CharSequence charSequence) {
		this.charSequence = charSequence;
		Assert.isNotNull(this.charSequence, "charSequence cannot be null");
	}
	
	/**	
	 * 	转化长度单位
	 * 	<br>自定义转换单位
	 * 	<br>如果不足转换长度最低要求则返回0(1023KB转换M时 此时返回0 1024KB才返回1M, 1999KB转换M时返回1MB)
	 * 	<br>转换后需要获取字符串请使用{@link #toString()}, 获取长度请使用{@link #getLength()}
	 * 
	 * 	@author Pan
	 * 	@param 	toUnit	转换单位
	 * 	@return	DataSize
	 * 	@throws UnknownTypeException 如果未支持类型或解析不到单位则抛出
	 */
	public DataSize transform(DataSizeUnit toUnit) {
		String str = this.charSequence.toString();
		DataSizeUnit unit = DataSizeUnit.getUnit(str);
		this.toUnit = toUnit;
		this.len = transform(longValue(str, unit), unit, toUnit);
		return this;
	}
	
	/**	
	 * 	转换long值
	 * 	
	 * 	@author Pan
	 * 	@param 	unit	长度单位
	 * 	@return	Long
	 */
	private Long longValue(String str, DataSizeUnit unit) {
		return ClassConvertUtils.toWrapperLong(StringUtils.subStr(str, 0, str.length() - unit.value().length()));
	}
	
	/**	
	 * 	转换长度单位
	 * 	<br>自定义单位
	 * 	
	 * 	@author Pan
	 * 	@param 	len		长度
	 * 	@param 	unit	当前单位
	 * 	@param 	toUnit	转换单位
	 * 	@return	Long
	 * 	@throws UnknownTypeException 如果未支持类型或解析不到单位则抛出
	 */
	private Long transform(long len, DataSizeUnit unit, DataSizeUnit toUnit) {
		switch (toUnit) {
			case BYTE:
				return unit.toByte(len);
			case KB:
				return unit.toKiloByte(len);
			case MB:
				return unit.toMegaByte(len);
			case GB:
				return unit.toGigaByte(len);
			case TB:	
				return unit.toTeraByte(len);
			case PB:
				return unit.toPetaByte(len);
			default:
				throw new UnknownTypeException("unsupported unit types");
		}
	}
	
	@Override
	public String toString() {
		if (toUnit == null) {
			return this.charSequence.toString();
		}
		return StringUtils.createBuilder(this.len).append(toUnit.value()).toString();
	}
	
	/**	
	 * 	返回的是转化后的长度
	 * 	
	 * 	@author Pan
	 * 	@return	Long
	 */
	public Long getLength() {
		return this.len;
	}
	
	/**	
	 * 	
	 * 	构建-自定义字符序列
	 * 	<br>如果需要转化请使用{@link DataSize#transform(DataSizeUnit)}
	 * 	<br>转换后需要获取字符串请使用{@link DataSize#toString()}, 获取长度请使用{@link DataSize#getLength()}
	 * 
	 * 	@author Pan
	 * 	@param 	charSequence	字符序列
	 * 	@return	DataSize
	 */
	public static DataSize create(CharSequence charSequence) {
		return new DataSize(charSequence);
	}
	
	/**	
	 * 	转化长度单位
	 * 	<br>自定义转换单位
	 * 	<br>动态解析长度单位(B, KB, MB, GB, TB, PB)
	 * 	<br>如果不足转换长度最低要求则返回0(1023KB转换M时 此时返回0 1024KB才返回1M, 1999KB转换M时返回1MB)
	 * 	<br>转换后需要获取字符串请使用{@link #toString()}, 获取长度请使用{@link #getLength()}
	 * 	<br>注意：不能没有带单位
	 * 	
	 * 	@author Pan
	 * 	@param 	charSequence	字符序列
	 * 	@param 	toUnit			转换单位
	 * 	@return	DataSize
	 * 	@throws UnknownTypeException 如果未支持类型或解析不到单位则抛出
	 */
	public static DataSize transform(CharSequence charSequence, DataSizeUnit toUnit) {
		return new DataSize(charSequence).transform(toUnit);
	}
	
	/**	
	 * 	默认当前长度转化为byte单位
	 * 	<br>动态解析长度单位(B, KB, MB, GB, TB, PB)
	 * 	<br>如果不足转换长度最低要求则返回0(1023KB转换M时 此时返回0 1024KB才返回1M, 1999KB转换M时返回1MB)
	 * 	<br>转换后需要获取字符串请使用{@link #toString()}, 获取长度请使用{@link #getLength()}
	 * 	
	 * 	@author Pan
	 * 	@param 	charSequence	字符序列
	 * 	@return	DataSize
	 * 	@throws UnknownTypeException 如果解析不到单位则抛出
	 */
	public static DataSize transform(CharSequence charSequence) {
		return transform(charSequence, DataSizeUnit.BYTE);
	}
}
